[superlog] Resolve domain names to UUID before creating annotations#475
[superlog] Resolve domain names to UUID before creating annotations#475superlog-app[bot] wants to merge 1 commit into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
2 Skipped Deployments
|
|
The latest updates on your projects. Learn more about Unkey Deploy
|
Greptile SummaryThis PR fixes annotation tool failures where the AI model passed a domain name (e.g.
Confidence Score: 3/5The core fix is correct and well-tested, but the domain comparison in resolveToolWebsite is case-sensitive; a mixed-case domain from the model would bypass the new slow-path and re-produce the original NOT_FOUND error. The resolver's domain lookup uses strict === equality. Domain names are case-insensitive by RFC 1035, and the AI model may reproduce the domain with any casing it observes in query results. If the stored domain is app.quiver.ai and the model passes App.Quiver.AI, the slow-path miss silently re-throws, leaving the annotation tools broken for that casing variant — the same symptom this PR was written to fix. packages/ai/src/ai/tools/utils/context.ts — the domain comparison and the fallback ctx.websiteDomain check both need case-normalisation before the fix is unconditionally reliable. Important Files Changed
Sequence DiagramsequenceDiagram
participant AI as AI Model
participant AT as annotations.ts
participant RW as resolveToolWebsite
participant RPC as RPC (annotations.create / list)
AI->>AT: "websiteId = "app.quiver.ai""
AT->>RW: resolveToolWebsite(ctx, "app.quiver.ai")
Note over RW: Fast path: UUID match in accessibleWebsites or ctx.websiteId?
RW-->>RW: No UUID match found
Note over RW: Slow path: domain match in accessibleWebsites?
RW-->>RW: "w.domain === "app.quiver.ai" → found web_uuid_123"
RW-->>AT: "{ websiteId: "web_uuid_123", domain: "app.quiver.ai" }"
AT->>RPC: "websiteId = "web_uuid_123""
RPC-->>AT: success
AT-->>AI: annotation created
Reviews (1): Last reviewed commit: "[superlog] Resolve domain names to UUID ..." | Re-trigger Greptile |
| const byDomain = accessible.find( | ||
| (w) => w.domain != null && w.domain === inputWebsiteId | ||
| ); | ||
| if (byDomain) { | ||
| return { websiteId: byDomain.id, domain: byDomain.domain ?? undefined }; | ||
| } | ||
| if (inputWebsiteId === ctx.websiteDomain && ctx.websiteId) { |
There was a problem hiding this comment.
Domain matching is case-sensitive via
===, but RFC 1035 treats hostnames as case-insensitive. If the AI model passes a domain with different casing than what is stored (e.g. App.Quiver.AI vs app.quiver.ai), the slow-path lookup silently falls through to the error throw, leaving the original bug intact for that casing variant. Normalising both sides to lowercase costs nothing and makes the fix robust.
| const byDomain = accessible.find( | |
| (w) => w.domain != null && w.domain === inputWebsiteId | |
| ); | |
| if (byDomain) { | |
| return { websiteId: byDomain.id, domain: byDomain.domain ?? undefined }; | |
| } | |
| if (inputWebsiteId === ctx.websiteDomain && ctx.websiteId) { | |
| const normalised = inputWebsiteId.toLowerCase(); | |
| const byDomain = accessible.find( | |
| (w) => w.domain != null && w.domain.toLowerCase() === normalised | |
| ); | |
| if (byDomain) { | |
| return { websiteId: byDomain.id, domain: byDomain.domain ?? undefined }; | |
| } | |
| if (ctx.websiteDomain?.toLowerCase() === normalised && ctx.websiteId) { |
| (w) => w.domain != null && w.domain === inputWebsiteId | ||
| ); | ||
| if (byDomain) { | ||
| return { websiteId: byDomain.id, domain: byDomain.domain ?? undefined }; |
There was a problem hiding this comment.
The
?? undefined is dead code here: the find predicate already guards w.domain != null, so byDomain.domain is always a non-null string at this point. The nullish coalescing just adds noise.
| return { websiteId: byDomain.id, domain: byDomain.domain ?? undefined }; | |
| return { websiteId: byDomain.id, domain: byDomain.domain }; |
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Summary
The insights agent's
create_annotation(andlist_annotations) tool failed withNOT_FOUND: website not foundwhenever the AI model passed a domain name (e.g.app.quiver.ai) instead of a UUID aswebsiteId. This happened consistently because the model observes the domain being used successfully aswebsiteIdin ClickHouse SQL queries — and reused it verbatim when calling the annotation tool.The annotation tool passed the raw AI-supplied string directly to the RPC, which does a PostgreSQL UUID-only lookup. Since domain names never match, every annotation creation attempt silently failed across all scheduled insights runs.
The fix has two parts:
resolveToolWebsite(utils/context.ts) — extends the existing resolver to also match by domain name against the accessible-websites list, and byctx.websiteDomainfor the single-site insights context.annotations.ts— wiresresolveToolWebsiteinto bothlist_annotationsandcreate_annotation, so the resolved UUID is used for all RPC calls.An alternative approach would be to add a domain→UUID lookup server-side in the RPC
annotations.createhandler. That would be more defensive but silently accepts incorrect input; the client-side resolution keeps the tool consistent withexecute_sqlandweb_metricsand catches misconfigured contexts early.Incident on Superlog
Was this PR helpful? Leave feedback — goes straight to the Superlog team.
Summary by cubic
Resolve domain names to UUIDs before calling annotation RPCs to stop "website not found" errors when a domain is used as
websiteId. Annotations can now be listed and created reliably in scheduled insights.resolveToolWebsiteto map domains to UUIDs via accessible websites orctx.websiteDomain.list_annotationsandcreate_annotationto use the resolved UUID for RPC calls.Written for commit edfeeda. Summary will update on new commits.